package map;

import images.ImageLoader;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.LinkedList;

import math.Vector2D;

public class PlayingField
{
	/**
	 * @param filename The file location of the file containing a PlayingField
	 * @return The PlayingField located in the specified file
	 */
	public static PlayingField load( String filename )
	{
		return null;
	}

	/**
	 * GrassTile
	 */
	public static ImageLoader tileG = new ImageLoader( "Images/grassTile.png" );
	public static ImageLoader tileB = new ImageLoader( "Images/brownTile.png" );
	
	/**
	 * The two-dimensional array of tiles
	 */
	private Tile[][] map;
	/**
	 * Width and height of the PlayingField
	 */
	private int width, height;
	
	private Polygon p;

	/**
	 * Constructor
	 * 
	 * @param width The number of tiles on the x axis
	 * @param height The number of tiles on the y axis
	 */
	public PlayingField( int width, int height )
	{
		map = new Tile[width][height];
		
		this.width = width;
		this.height = height;
		
		for( int i = 0; i < width; i++ )
		{
			for( int j = 0; j < height; j++ )
			{
				map[i][j] = new Tile( tileG, fromGridToWorld( new Vector2D( i, j ) ) );
			}
		}
		p = new Polygon();
		p.addPoint( 16, 0 );
		p.addPoint( 32, 8 );
		p.addPoint( 16, 15 );
		p.addPoint( 0, 8 );
	}
	
	public BufferedImage getBirdsEyeView()
	{
		BufferedImage img = new BufferedImage( width, height, BufferedImage.TYPE_4BYTE_ABGR );
		
		Graphics2D g = img.createGraphics();
		
		for( int i = 0; i < width; i++ )
		{
			for( int j = 0; j < height; j++ )
			{
				if( map[i][j].getImage().equals( tileG ) )
					g.setColor( Color.green );
				else if( map[i][j].getImage().equals( tileB ) )
					g.setColor( new Color( 139, 69, 19 ) );
				
				g.fillRect( i, j, 1, 1 );
			}
		}
		
		return img;
	}
	
	/**
	 * @param g Graphics used to draw the PlayingField's tiles
	 */
	public void render( Graphics2D g )
	{
		Vector2D v;

		g.setColor( new Color( 255, 0, 0, 128 ) );
		
		for( int i = 0; i < width; i++ )
		{
			for( int j = 0; j < height; j++ )
			{
				map[i][j].render( g );

				if( map[i][j].isOccupied() )
				{
					v = map[i][j].getLocation();
					g.setTransform( AffineTransform.getTranslateInstance( v.getX() - 16, v.getY() - 8 ) );
					g.fillPolygon( p );
					g.setTransform( new AffineTransform() );
				}
			}
		}
	}
	
	/**
	 * @param filename Location to save the PlayingField to
	 */
	public void save( String filename )
	{
		
	}
	
	public int getWidth()
	{
		return width;
	}
	
	public int getHeight()
	{
		return height;
	}
	
	public Tile getTile( int x, int y )
	{
		return map[x][y];
	}
	
	/**
	 * @param locInGridCoord A location on the grid ( <1, 1> instead of <32, 32> )
	 * @return The tile at that location
	 */
	public Tile getTile( Vector2D locInGridCoord )
	{
		if( isValid( locInGridCoord ) )
			return map[(int)locInGridCoord.getX()][(int)locInGridCoord.getY()];
		return null;
	}
	
	/**
	 * @param t The tile to evaluate
	 * @return A list of all neighbors of the tile
	 */
	public LinkedList<Tile> getNeighbors( Tile t )
	{
		LinkedList<Tile> neighbors = new LinkedList<Tile>();
		
		for( int i = 0; i < 360; i += 45 )
		{
			Tile tile = getAdjacentTile( t.getLocation(), i );
			
			if( tile != null )
				neighbors.add( tile );
		}
		
		return neighbors;
	}
	
	/**
	 * @param from The first tile
	 * @param to The tile to get the angle to
	 * @return The angle between the two tiles
	 */
	public static int getAngle( Vector2D from, Vector2D to )
	{
		from = fromWorldToGrid( from );
		to = fromWorldToGrid( to );
		
		int xC = (int)( from.getX() - to.getX() );
		int yC = (int)( from.getY() - to.getY() );
		
		if( xC == 0 && yC == -2 )
			return 0;
		else if( ( xC == 1 || xC == 0 ) && yC == -1  )
			return 45;
		else if( xC == 1 && yC == 0 )
			return 90;
		else if( ( xC == 1 || xC == 0 ) && yC == 1  )
			return 135;
		else if( xC == 0 && yC == 2  )
			return 180;
		else if( ( xC == -1 || xC == 0 ) && yC == 1  )
			return 225;
		else if( xC == -1  && yC == 0  )
			return 270;
		else
			return 315;
		
		/* Computationally expensive but accurate and concise.
		 * 
		Vector2D up = new Vector2D( 0, 1 );
		Vector2D dir = new Vector2D( xC, yC );
		
		return (int)Math.acos( Vector2D.dotProduct( up, dir ) / ( up.getMagnitude() * dir.getMagnitude() ) );
		*/
	}
	
	/**
	 * @param t
	 * @param dir
	 * @return
	 */
	public Tile getAdjacentTile( Vector2D t, int dir )
	{
		t = fromWorldToGrid( t );
		
		int x = (int)t.getX();
		int y = (int)t.getY();
		
		dir %= 360;
		
		if( dir == 0 )
		{
			y -= 2;
		}
		else if( dir == 45 )
		{
			x = x % 2 != 0 ? x + 1 : x;
			y -= 1;
		}
		else if( dir == 90 )
		{
			x += 1;
		}
		else if( dir == 135 )
		{
			x = x % 2 != 0 ? x + 1 : x;
			y += 1;	
		}
		else if( dir == 180 )
		{
			y += 2;
		}
		else if( dir == 225 )
		{
			x = x % 2 == 0 ? x - 1 : x;
			y += 1;
		}
		else if( dir == 270 )
		{
			x -= 1;
		}
		else if( dir == 315 )
		{
			x = x % 2 == 0 ? x - 1 : x;
			y -= 1;	
		}
		
		if( isValid( x, y ) )
			return map[x][y];
		else
			return null;
	}
	
	/**
	 * 
	 * @param x The x value in grid coordinates
	 * @param y The y value in grid coordinates
	 * @return true if the x value is between 0 and the width
	 * 		   and the y value is between 0 and the height, 
	 * 		   false otherwise
	 */
	public boolean isValid( int x, int y )
	{
		return !( x < 0 || x >= width || y < 0 || y >= height );
	}
	
	public boolean isValid( Vector2D v )
	{
		return !( v.getX() < 0 || v.getX() >= width || v.getY() < 0 || v.getY() >= height );
	}

	/**
	 * @param v The vector in world coordinates to change
	 * @return A vector on the grid in world coordinates
	 */
	public static Vector2D fromWorldToWorldOnGrid( Vector2D v )
	{
		int modY = (int)v.getY() % 8;
		int y = (int)v.getY() - modY;
		
		if( modY > 4 )
			y += 8;

		int xSub = y % 16 != 0 ? 0 : 16;
		int modX = (int)( v.getX() + xSub ) % 32;
		int x = (int)v.getX() + xSub - modX;
		
		if( modX > 16 )
			x += 32;
		
		x = y % 16 != 0 ? x : x - 16;
		
		return new Vector2D( x, y );
	}
	
	/**
	 * @param v The vector in world coordinates
	 * @return A vector in grid coordinates
	 */
	public static Vector2D fromWorldToGrid( Vector2D v )
	{
		v = fromWorldToWorldOnGrid( v );
		
		return new Vector2D( ( v.getY() % 16 != 0 ? v.getX() : v.getX() + 16 ) / 32, v.getY() / 8 );
	}
	
	/**
	 * @param x1 The x value of a grid coordinate
	 * @param y1 The y value of a grid coordinate
	 * @return The world coordinates of the grid
	 */ 
	public static Vector2D fromGridToWorld( int x1, int y1 )
	{
		int y = y1 * 8;
		int x = x1 * 32;
		x = y % 16 != 0 ? x : x - 16;
		
		return new Vector2D( x, y );
	}
	
	/**
	 * @param v The vector in grid coordinates
	 * @return The world coordinates of the grid
	 */
	public static Vector2D fromGridToWorld( Vector2D v )
	{
		int y = (int)v.getY() * 8;
		int x = (int)v.getX() * 32;
		x = y % 16 != 0 ? x : x - 16;
		
		return new Vector2D( x, y );
	}
}
